#define COEF2
#define OPTMIZATION_PIXELACCESS

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace GridRegulator
{
    public static class ColorConverter
    {
#if COEF1
        const double kCR = 0.257;
        const double kCG = 0.504;
        const double kCB = 0.098;
        const double kBias = 16;
#elif COEF2
        const double kCR = 0.299;
        const double kCG = 0.587;
        const double kCB = 0.114;
        const double kBias = 0;
#else

#error "No coeff specified"

#endif


        public static GrayImage BitmapToGray(Bitmap bitmap)
        {
            GrayImage gray = new GrayImage(bitmap.Width, bitmap.Height);

#if OPTMIZATION_PIXELACCESS

            BitmapData bmd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);

            try
            {
                unsafe
                {
                    byte* row = (byte*)bmd.Scan0;

                    if (bitmap.PixelFormat == PixelFormat.Format24bppRgb || 
                        bitmap.PixelFormat == PixelFormat.Format32bppRgb)
                    {
                        int pelbytes = bitmap.PixelFormat == PixelFormat.Format24bppRgb ? 3 : 4;

                        for (int i = 0; i < bitmap.Height; i++)
                        {
                            for (int j = 0; j < bitmap.Width; j++)
                            {
                                int r = row[j * pelbytes];
                                int g = row[j * pelbytes + 1];
                                int b = row[j * pelbytes + 2];

                                double y = Math.Round(kCR * r + kCG * g + kCB * b + kBias);
                                if (y < 0) y = 0;
                                else if (y > 255) y = 255;
                                gray[i, j] = (byte)y;
                            }
                            row += bmd.Stride;
                        }
                    }
                    else if (bitmap.PixelFormat == PixelFormat.Format32bppArgb)
                    {
                        for (int i = 0; i < bitmap.Height; i++)
                        {
                            for (int j = 0; j < bitmap.Width; j++)
                            {
                                int r = row[j * 4 + 1];
                                int g = row[j * 4 + 2];
                                int b = row[j * 4 + 3];

                                double y = Math.Round(kCR * r + kCG * g + kCB * b + kBias);
                                if (y < 0) y = 0;
                                else if (y > 255) y = 255;
                                gray[i, j] = (byte)y;
                            }
                            row += bmd.Stride;
                        }
                    }
                    else
                    {
                        throw new ApplicationException("Unsupported bitmap pixel type");
                    }
                }
            }
            finally
            {
                bitmap.UnlockBits(bmd);
            }

#else
            
            for (int i = 0; i < bitmap.Height; i++)
            {
                for (int j = 0; j < bitmap.Width; j++)
                {
                    Color color = bitmap.GetPixel(j, i);
#if USE_HSB

                    double y = color.GetBrightness();
                    gray[i, j] = (byte)(255 * y);

#else
                    double y = Math.Round(kCR * color.R + kCG * color.G 
                        + kCB * color.B + kBias);
                    if (y < 0) y = 0;
                    else if (y > 255) y = 255;
                    gray[i, j] = (byte)y;
#endif
                }
            }

#endif

            return gray;
        }

        public static Bitmap GrayToBitmap(GrayImage gray)
        {
            Bitmap bitmap = new Bitmap(gray.Width, gray.Height);

            for (int i = 0; i < bitmap.Height; i++)
            {
                for (int j = 0; j < bitmap.Width; j++)
                {
                    double y = (double)gray[i, j];
                    double r, g, b;

                    r = y;
                    g = y;
                    b = y;

                    Color color = Color.FromArgb((int)r, (int)g, (int)b);

                    bitmap.SetPixel(j, i, color);
                }
            }

            return bitmap;
        }
    }
}
